/**
 * 
 */
package algoritmos;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.Vector;

import matrices.centro.Clase;
import Jama.Matrix;
import problema.FormaProblema;
import problema.Utilidades;

/**
 * @author Jose Antonio Gonzalez, Ambrin Chaudhary
 * 
 */
public class SOM implements FormaProblema {

	public double aprend = 1.0;
	public double alfaIni = 1.0;
	public double alfaFin = 0.8;
	public int kMax = 5;
	public double alfak;
	public double T = 0.2;
	public double epsilon = 0.001;
	public boolean continuar = true;
	
	public int lonVect = 0;

	public Vector<Matrix> vectorMuestras;
	public Vector<Clase> vectorCentros;
	public Vector<Clase> vectorCentConverg;

	//private double alfaK(int k) {
	public double alfaK(int k) {
		double s = 0;
		double kaux = k;
		s = Math.pow((alfaFin / alfaIni), (kaux / kMax));
		s *= alfaIni;
		return s;
	}

	//private double regionDeVecindad(Matrix m1, Matrix m2, int k) {
	public double regionDeVecindad(Matrix m1, Matrix m2, int k) {
		double rv = 0.0;
		double aux = 0.0-Utilidades.norma2alcuadrado(m1, m2);
		//double aux = -Utilidades.dist(m1, m2);
		
		aux /= 2 * alfaK(k);
		rv = Math.exp(aux);

		return rv;
	}

	//private double razonAprendizaje(int k) {
	public double razonAprendizaje(int k) {
		return (1.0 / (10.0 + k));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see problema.FormaProblema#cargarClases(java.util.Vector,
	 *      java.util.Vector)
	 */
	public void cargarClases(Vector<Clase> inClass, Vector<Matrix> inMuestras) 
	{
		this.vectorCentros = inClass;
		this.vectorMuestras = inMuestras;
		
		// limpiamos los vectores de indices a muestras
		for(int i =0; i< this.vectorCentros.size(); i++)
		{
			this.vectorCentros.get(i).deleteAllElemetns();
		}
		
		if(this.vectorMuestras.size() > 0)
        	this.lonVect = this.vectorMuestras.get(0).getColumnDimension();
        else
        	this.lonVect = 0;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see problema.FormaProblema#cargarFichero(java.io.BufferedReader)
	 */
	public void cargarFichero(BufferedReader inStream) throws IOException 
	{
		this.vectorCentros = new Vector<Clase>();
		this.vectorMuestras = new Vector<Matrix>();

		Clase clase = null;
		boolean leerCentro = false;

		try {

			// BufferedReader entrada = new BufferedReader(new
			// FileReader(fichero));

			boolean primeralinea = true;
			
			String s = "";
			do {
				s = inStream.readLine();
				if (s == null)
					break;
				else {
					if (leerCentro) {
						StringTokenizer st = new StringTokenizer(s, ",");

						
						if(primeralinea)
						{
							this.lonVect = st.countTokens();
							primeralinea = false;
						}
						
						
						int i = 0;
						int cant = st.countTokens();
						double d[][] = new double[1][cant];
						while (st.hasMoreTokens()) {

							String s1 = st.nextToken();
							double aux = Double.parseDouble(s1);
							d[0][i] = aux;
							i++;
						}
						clase.setCentro(new Matrix(d));
						this.vectorCentros.add(clase);
						leerCentro = false;
					}

					else if (s.contains("centro:")) 
					{
						clase = new Clase();
						leerCentro = true;
					
					} 
					else 
					{
						StringTokenizer st = new StringTokenizer(s, ",");

						
						if(primeralinea)
						{
							this.lonVect = st.countTokens();
							primeralinea = false;
						}
						
						
						int i = 0;
						int cant = st.countTokens();
						double d[][] = new double[1][cant];
						while (st.hasMoreTokens()) {

							String s1 = st.nextToken();
							double aux = Double.parseDouble(s1);
							d[0][i] = aux;
							i++;
						}
						Matrix m = new Matrix(d);
						if (clase != null)
							clase.addElement(m);

						this.vectorMuestras.add(m);
					}
				}
			} while (true);

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("Error al cargar archivo");
			throw e;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see problema.FormaProblema#ciclo(java.util.Vector)
	 */
	public Clase ciclo(Vector<Double> m) {
		
		Matrix matriz = Utilidades.toMatrix(m);
		Matrix c = null ;
		int centro = -1; 
		double min = Double.MAX_VALUE;
		
		for (int i = 0; i < this.vectorCentros.size(); i++) 
		{
			Clase clase = this.vectorCentros.get(i);
			Matrix m1 = clase.getCentro();

			double aux = this.regionDeVecindad(matriz, m1, 1);
			min = Math.min(min, aux);
			if(min == aux)
			{
				centro = i;
				c = m1;
			}
			
			
		}
		Matrix cAux =this.actualizarCentro(c, matriz, 1);
		this.vectorCentros.get(centro).setCentro(cAux);
		this.vectorCentros.get(centro).addElement(matriz);
		return vectorCentros.get(centro);
	}

	private void ClonarVectConverg() {
		this.vectorCentConverg = new Vector<Clase>();
		for (int i = 0; i < this.vectorCentros.size(); i++) {
			Clase c1 = new Clase();
			Matrix c2 = this.vectorCentros.get(i).getCentro();

			c1.setCentro(Matrix.constructWithCopy(c2.getArray()));
			this.vectorCentConverg.add(c1);
		}
	}
	
	private Matrix actualizarCentro(Matrix c, Matrix xi, int k) {
		Matrix minus = xi.minus(c);
		double aux = this.razonAprendizaje(k) * this.regionDeVecindad(xi, c, k);
		Matrix mul = minus.times(aux);
		Matrix resul = c.plus(mul);
		return resul;
	}

	private void evaluarCentros(int k) 
	{
		int centro = -1;
		Matrix c = null;
		for (int j = 0; j < this.vectorMuestras.size(); j++) 
		{
			Matrix xi = this.vectorMuestras.get(j);
			double min = Double.MAX_VALUE;
			double aux;
			for (int i = 0; i < this.vectorCentros.size(); i++) 
			{
				Clase clase = this.vectorCentros.get(i);
				Matrix m = clase.getCentro();

				aux = this.regionDeVecindad(xi, m, k);
				min = Math.min(min, aux);
				if(min == aux)
				{
					centro = i;
					c = m;
				}
				
				
			}
			Matrix cAux =this.actualizarCentro(c, xi, k);
			this.vectorCentros.get(centro).setCentro(cAux);
			this.vectorCentros.get(centro).addElement(xi);
		}
	}
	
	private void evaluarConvergencia()
	{
		for (int j = 0; j < vectorCentros.size(); j++)
		{
			Matrix c1 = this.vectorCentConverg.get(j).getCentro();
			Matrix c2 = this.vectorCentros.get(j).getCentro();

			if (Utilidades.dist(c1, c2) < this.epsilon) {
				this.continuar = false;
			} else
				this.continuar = true;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see problema.FormaProblema#entrenar(java.util.Vector)
	 */
	public void entrenar(Vector<Double> listaParametros) {
		this.alfaIni = listaParametros.get(1);
		this.alfaFin = listaParametros.get(2);
		this.T = listaParametros.get(4);
		this.kMax = (int) ((double) listaParametros.get(3));

		for (int i = 0; i < this.kMax; i++) 
		{
			if(this.continuar)
			{
				this.ClonarVectConverg();
				this.evaluarCentros(i);
				this.evaluarConvergencia();
			}
			else
				break;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see problema.FormaProblema#getClases()
	 */
	public Vector<Clase> getClases() {
		return this.vectorCentros;
	}

	
	public String toStringCentros() {
		String s = "";
		Clase c = null;
		for (int i = 0; i < this.vectorCentros.size(); i++) {
			s += this.vectorCentros.get(i).toString() + "\n";
			// Utilidades.matrixToString(c.getCentro()) + "\n";
		}

		return s;
	}

	public String toStringMatrices() {
		String s = "";
		Matrix m = null;
		for (int i = 0; i < this.vectorMuestras.size(); i++) {
			m = this.vectorMuestras.get(i);
			s += Utilidades.matrixToString(m) + "\n";
		}

		return s;
	}

        
	public int longVect() {
		
		return this.lonVect;
	}
	
}
